[Chapter 31] 31.5 Macro Expansion: $ and $&
Author: Bryan Costales & Eric Allman
Date: 1999-01-06T20:10:55Z

#

| Chapter 31 Defined Macros |

## 31.5 Macro Expansion: $ and $&

The value of a macro can be used by putting a `$` character in front of the macro's name. For example, consider the following definition:

>     DXtext

Here, the macro named `X` is given `text` as its value.

If you later prefix a macro name with a `$` character, you may use that value. This is called expanding a macro:

>     $X

Here, the expression `$X` tells sendmail to use the value stored in `X` (the `text`) rather than its name (`X`).

For multicharacter names, the process is the same, but the name is surrounded with curly braces:

>     D{Xxx}text         declare {Xxx}
>     ${Xxx}             use {Xxx}

### 31.5.1 Macro Expansion Is Recursive

When `text` contains other macros, those other macros are also expanded. This process is recursive and continues until all macros have been expanded. For example, consider the following:

>     DAxxx
>     DByyy
>     DC$A.$B
>     DD$C.zzz

Here, the `text` for the macro `D` is `$C.zzz`. When the expression `$D` appears in a line in the configuration file, it is recursively expanded like this:

>     $D             becomes   $C.zzz
>     $C.zzz         becomes   $A.$B.zzz
>     $A.$B.zzz      becomes   xxx.$B.zzz
>     xxx.$B.zzz     becomes   xxx.yyy.zzz

Notice that when sendmail recursively expands a macro, it does so one macro at a time, always expanding the leftmost macro.

In rules, when sendmail expands a macro, it also tokenizes it. For example, placing the above `$D` in the following rule's LHS:

>     R$+@$D      $1

causes the LHS to contain seven tokens, rather than three:

>     R$+@xxx.yyy.zzz       $1

### 31.5.2 When Is a Macro Expanded?

A macro can either be expanded immediately or at runtime, depending on where the expansion takes place in the configuration file.

Macros are expanded in rule sets as the configuration file is read and parsed by sendmail, and (beginning with V8.7) so are macros in rule-set names (see Section 29.1.4, "Macros in Rule-Set Names") and in maps declared with the `K` configuration command (see Section 33.3, "The K Configuration Command"). In other configuration lines, expansion is deferred until sendmail actually needs to use that value. In yet others, macros are neither recognized nor expanded.

To illustrate, macros used in header commands are not be expanded until the headers of a mail message are processed:

>     H?x?Full-Name: $x

Here, `$x` (see Section 31.10.42, $x) may change as sendmail is running. It contains as its value the full name of the sender. Clearly, this macro should not be expanded until that full name is known.

On the other hand, macros in rules are always expanded when the configuration file is read. Therefore macros like `$x` should never be used in rules, because the configuration file is read long before mail is processed:

>     R$x	($x)

Rules like this won't work because `$x` lacks a value when the configuration file is read. This rule will be expanded to become meaningless:

>     R      ()

Note that the `$``digit` positional operator (see Section 28.6.1, "Copy by Position: $digit") in the RHS may not be used to reference defined macros in the LHS. Consider this example, in which `{HOST}` has the value `myhost`:

>     R${HOST}     <$1>

The `${HOST}` is expanded when the configuration file is read and is transformed into:

>     Rmyhost   <$1>    error

Here, the `$1` has no wildcard operator in the LHS to reference and so will produce this error:

>     replacement $1 out of bounds

### 31.5.3 Use Value as Is with $&

For those situations in which a macro should not be recursively expanded, but rather should be used in rules as is, V8 sendmail offers the `$&` prefix. For example, consider the following RHS of a rule:

>     R...     $w.$&m

When sendmail encounters this RHS in the configuration file, it recursively expands `$w` into its final text value (where that text value is your hostname, such as lady). But because the `m` macro is prefixed with `$&`, it is not expanded.

This could be useful, because it appears to offer a way to delay expansion of macros in rules until after the configuration file is read. Unfortunately such is not always the case, because the expanded text returned by the `$&` prefix is always a single token. That is, because the above is tokenized before each token is evaluated, it appears in the workspace as

>     lady . $m

Here, the `$m` will expand to its current value, say, our.domain, but that expansion will remain a single token:

>     lady . our.domain

When tokens are compared during rule-set processing, they are compared token by token. Consequently, the single token above will not match the individual tokens of a real address, as shown on the left:

>     our      does not match our.domain
>     .        does not match our.domain
>     domain   does not match our.domain

The `$&` prefix is intended to provide a way to access macros that are given values after the configuration file is read. Therefore the failure of `$&` to recursively expand is the result of an implementation designed to meet the limited goal of accessing those runtime macros. (See Section 33.8.4, dequote for ways to use the `dequote` database class to circumvent this restriction.)

To illustrate one application of `$&`, consider the client/hub setup described in the tutorial. In that setup, all mail sent from a client machine is forwarded to the hub for eventual delivery. If the client were to run a sendmail daemon to receive mail for local delivery, a mail loop could (in the absence of an MX record) develop where a message would bounce back and forth between the client and the hub, eventually failing.

To break such a loop, a rule must be devised that recognizes that a received message is from the hub:

>     R$+              $: $&r @ $&s <$1>       Get protocol and host
>     Rsmtp @ $H <$+>  $#local $: $1           Local delivery breaks a loop
>     R$* <$+>         $#smtp  $@ $H $: $2     Punt to hub

These rules appear in rule set 0. By the time they are reached, other rules have forwarded any nonlocal mail to the hub. What is left in the workspace is a lone username. The first rule above matches the workspace and rewrites it to be the sending protocol (`$&r`; see Section 31.10.31, $r), an `@`, the sending host (`$&s`, see Section 31.10.33, $s), and the username in angle brackets:

>     user     becomes     smtp@hub<user>

The second rule checks to make sure the message was received with the SMTP protocol from the hub. If it was, then the `local` delivery agent is used to deliver the message on the local machine. If it was received from any other host or by any other protocol, the second rule fails and the third forwards the lone user address to the hub.

31.4 Macro Names |  | 31.6 Macro Conditionals: $?, $|, and $.